﻿using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;

namespace DynamicWebApi.SourceGenerator
{
    [Generator]
    public class ControllerGenerator : ISourceGenerator
    {

        private readonly HashSet<string> httpMethodAttributeNames = new HashSet<string>()
        {
            HttpMethodConstants.HttpGet,
            HttpMethodConstants.HttpPost,
            HttpMethodConstants.HttpPut,
            HttpMethodConstants.HttpDelete,
            HttpMethodConstants.HttpPatch,
            HttpMethodConstants.HttpHead,
            HttpMethodConstants.HttpAction,
            HttpMethodConstants.HttpOptions,
        };
        public void Initialize(GeneratorInitializationContext context)
        {
            context.RegisterForSyntaxNotifications(() => new AppServiceSyntaxReceiver());
        }

        public void Execute(GeneratorExecutionContext context)
        {
            var receiver = context.SyntaxContextReceiver;

            if (receiver is AppServiceSyntaxReceiver appServiceSyntaxReceiver)
            {
                var appServices = appServiceSyntaxReceiver.AppServices;

                if (appServices == null)
                {
                    return;
                }

                foreach (var appService in appServices)
                {
                    var namespaceDeclarationSyntax = appService.Ancestors().OfType<NamespaceDeclarationSyntax>().FirstOrDefault();
                    // 生成命名空间名称
                    var namespaceName = GeneratorNamespaceName(namespaceDeclarationSyntax);
                    // 类型名称
                    var name = appService.Identifier.ValueText;

                    context.AddSource($"{name}Controller.cs", SourceText.From($@"
                    using Microsoft.AspNetCore.Mvc;
                    namespace {namespaceName}
                    {{
                        [Route(""api/[Controller]/[Action]"")]
                        [ApiController]
                        public class {name}Controller
                        {{
                            {GetMethods(appService)}
                        }}
                    }}
                    ", Encoding.UTF8));
                }
            }
        }

        private static string GeneratorNamespaceName(NamespaceDeclarationSyntax namespaceDeclarationSyntax)
        {
            var namespaceName = namespaceDeclarationSyntax?.Name.ToString();
            namespaceName = string.IsNullOrWhiteSpace(namespaceName) ? "NullNamespaceWithAutoGenerator" : namespaceName;
            return namespaceName;
        }

        private string GetMethods(ClassDeclarationSyntax classDeclarationSyntax)
        {
            var sb = new StringBuilder();
            foreach (var mds in classDeclarationSyntax.Members.OfType<MethodDeclarationSyntax>())
            {
                sb.Append($@"
                    {GetAttribute(mds)}
                    public {mds.ReturnType} {mds.Identifier.ValueText}{mds.ParameterList}
                    {{
                       {mds.Body}
                    }}");
            }

            return sb.ToString();
        }

        private string GetAttribute(MethodDeclarationSyntax mds)
        {
            var attributeLists = mds.AttributeLists.Select(x => $"{x}");

            var result = string.Join("\r\n", mds.AttributeLists);

            if (attributeLists.Intersect(httpMethodAttributeNames).Any())
                return result;

            return $"{result}\r\n[HttpGet]";
        }
    }

    public class AppServiceSyntaxReceiver : ISyntaxContextReceiver
    {
        private IList<ClassDeclarationSyntax> _allBaseAppServices = new List<ClassDeclarationSyntax>();

        private IList<ClassDeclarationSyntax> _appServices = new List<ClassDeclarationSyntax>();
        public IEnumerable<ClassDeclarationSyntax> AppServices => _appServices;

        public void OnVisitSyntaxNode(GeneratorSyntaxContext context)
        {
            var classDeclarationSyntax = context.Node as ClassDeclarationSyntax;
            if (classDeclarationSyntax == null)
            {
                return;
            }

            if (_allBaseAppServices.Any(x => x == classDeclarationSyntax))
            {
                return;
            }

            if (IsAppService(classDeclarationSyntax, context.SemanticModel))
            {
                _appServices.Add(classDeclarationSyntax);
            }
        }

        private bool IsAppService(ClassDeclarationSyntax classDeclarationSyntax, SemanticModel semanticModel)
        {
            var classSymbol = semanticModel.GetDeclaredSymbol(classDeclarationSyntax);

            if (classSymbol == null)
            {
                return false;
            }

            return GetAncestorInterfaces(classSymbol).Any(x => x.Name == nameof(IAppService));
        }

        private IEnumerable<INamedTypeSymbol> GetAncestorInterfaces(INamedTypeSymbol classSymbol)
        {
            var interfaces = new List<INamedTypeSymbol>();

            if (classSymbol.BaseType != null)
            {
                var baseType = classSymbol.DeclaringSyntaxReferences.Select(r => r.GetSyntax())
                    .OfType<ClassDeclarationSyntax>()
                    .FirstOrDefault();
                if (baseType != null)
                {
                    _allBaseAppServices.Add(baseType);
                }

                interfaces.AddRange(GetAncestorInterfaces(classSymbol.BaseType));
            }

            foreach (var @interface in classSymbol.Interfaces)
            {
                interfaces.Add(@interface);
                interfaces.AddRange(GetAncestorInterfaces(@interface));
            }

            return interfaces;
        }

        //private bool IsAppService(TypeDeclarationSyntax classDeclarationSyntax, SemanticModel semanticModel)
        //{
        //    var baseTypes = classDeclarationSyntax.BaseList?.Types;
        //    if (!baseTypes.HasValue)
        //    {
        //        return false;
        //    }

        //    var simpleBaseTypes = baseTypes.Value;
        //    foreach (var typeSyntax in simpleBaseTypes)
        //    {
        //        var baseTypeName = typeSyntax.Type.ToString();
        //        if (baseTypeName == nameof(IAppService))
        //        {
        //            return true;
        //        }

        //        var baseTypeSymbol = semanticModel.GetSymbolInfo(typeSyntax.Type).Symbol as INamedTypeSymbol;

        //        var baseTypeSyntaxNode = baseTypeSymbol.DeclaringSyntaxReferences.FirstOrDefault()?.GetSyntax();

        //        if (baseTypeSyntaxNode == null)
        //        {
        //            continue;
        //        }

        //        classDeclarationSyntax = baseTypeSyntaxNode.SyntaxTree.GetRoot().DescendantNodesAndSelf().OfType<TypeDeclarationSyntax>()
        //            .FirstOrDefault(x => x.Identifier.ValueText == baseTypeName);

        //        if (classDeclarationSyntax == default)
        //        {
        //            continue;
        //        }

        //        if (IsAppService(classDeclarationSyntax, semanticModel))
        //        {
        //            return true;
        //        }
        //    }

        //    return false;
        //}
    }
}


public interface IAppService
{
}